local coal = minetest.registered_aliases["coal"]
local COAL_HEAT = 10
local KILN_TIMER_LENGTH = 0.2





local update_infotext =
	dofile(minetest.get_modpath("kilns") .. "/infotext.lua")






--the act of smelting
------------------------------------------------------------------------

--consumes coal and increases heat if heat is <= 0,
--returns new heat, fuel is passed as a reference so it's not returned
local function refuel_if_necessary(heat, fuel)
	if heat <= 0 and not fuel:is_empty()
	then
		heat = COAL_HEAT
		fuel:take_item()
	end
	return heat
end

--looks up a smelting recipe for an input itemstack and returns
--the result or false if there is no recipe for the item stack.
local function get_smelt_result(input_stack)
	local input = {method = "cooking", items = {input_stack}}
	local result = minetest.get_craft_result(input)

	if not result.item:is_empty()
	then
		return result
	else
		return false
	end

end

local chimney_particle_prototype =
{
	amount = 20 * KILN_TIMER_LENGTH,
	time = KILN_TIMER_LENGTH,
	minvel = {x = -0.2, y = 0.2, z = -0.2},
	maxvel = {x = 0.2, y = 1, z = 0.2},
	minexptime = 3,
	maxexptime = 5,
	minsize = 1,
	maxsize = 3,
}

local function spawn_chimney_particles(pos)
	pos.y = pos.y + 0.5

	local smokedef_white = {}
	local smokedef_black = {}
	for k, v in pairs(chimney_particle_prototype)
	do
		smokedef_white[k] = v
		smokedef_black[k] = v
	end
	smokedef_white.minpos = pos
	smokedef_white.maxpos = pos
	smokedef_black.minpos = pos
	smokedef_black.maxpos = pos
	smokedef_white.texture = "smoke_puff_white.png"
	smokedef_black.texture = "smoke_puff_black.png"

	minetest.add_particlespawner(smokedef_white)
	minetest.add_particlespawner(smokedef_black)
end

local smoke_puff_particle_prototype =
{
	amount = 30,
	time = 0.05,
	minvel = {x = -1, y = 0.2, z = -1},
	maxvel = {x = 1, y = 0.5, z = 1},
	minexptime = 0.3,
	maxexptime = 1,
	minsize = 1,
	maxsize = 3,
}

local function spawn_smoke_puff_particles(pos)
	pos.y = pos.y + 0.5

	local smokedef_white = {}
	local smokedef_black = {}
	for k, v in pairs(smoke_puff_particle_prototype)
	do
		smokedef_white[k] = v
		smokedef_black[k] = v
	end
	smokedef_white.minpos = pos
	smokedef_white.maxpos = pos
	smokedef_black.minpos = pos
	smokedef_black.maxpos = pos
	smokedef_white.texture = "smoke_puff_white.png"
	smokedef_black.texture = "smoke_puff_black.png"

	minetest.add_particlespawner(smokedef_white)
	minetest.add_particlespawner(smokedef_black)
end



local function on_timer(pos, elapsed)

	local meta = minetest.get_meta(pos)
	local inv = meta:get_inventory()
	local heat = meta:get_float("heat") - elapsed
	local progress = meta:get_float("progress") - elapsed

	--the smelting result possibly won't fit if the recipe is changed
	--inbetween sessions but I don't care enough to fix it.

	local fuel = inv:get_stack("fuel", 1)
	local input = inv:get_stack("in", 1)
	local output = inv:get_stack("out", 1)

	--should smelt
	if not input:is_empty()
	then
		heat = refuel_if_necessary(heat, fuel)
		if heat > 0
		then
			progress = progress + 2 * elapsed
			local recipe = get_smelt_result(input)
			if recipe and progress >= recipe.time --done one item
			then
				spawn_smoke_puff_particles(pos)
				progress = 0
				local rest = output:add_item(recipe.item)
				local consume = input:take_item()
				--if output stack is full, undo smelting
				if not rest:is_empty()
				then
					input:add_item(consume)
				end
			end
		end
	end

	--write back possibly changed values
	local fuel = inv:set_stack("fuel", 1, fuel)
	local input = inv:set_stack("in", 1, input)
	local output = inv:set_stack("out", 1, output)
	meta:set_float("heat", heat)
	meta:set_float("progress", progress)




	local continue = heat > 0 or progress > 0

	update_infotext(meta, continue)
	if not continue
	then
		local node = minetest.get_node(pos)
		local n =
		{
			name = "kilns:kiln_off",
			param1 = node.param1,
			param2 = node.param2
		}
		minetest.swap_node(pos, n)
	else
		spawn_chimney_particles(pos)
	end
	return continue
end





--player interaction with kiln inventory
------------------------------------------------------------------------

--returning items from the kiln's inventory
-----------------------------------------------------
local function kiln_get_return_stack(kiln_inv)
	local priority_order = {"out", "in", "fuel",}
	for i, list in ipairs(priority_order)
	do
		local itemstack = kiln_inv:get_stack(list, 1)
		if not itemstack:is_empty()
		then
			return itemstack, list
		end
	end
end

local function kiln_item_return_wholestack(kiln_inv, player_inv)
	local stack, retlist = kiln_get_return_stack(kiln_inv)
	if not stack
	then
		--Empty
		return
	end
	local overflow = player_inv:add_item("main", stack)
	kiln_inv:set_stack(retlist, 1, overflow)
end
local function kiln_item_return_single(kiln_inv, player_inv)
	local stack, retlist = kiln_get_return_stack(kiln_inv)
	if not stack
	then
		--Empty
		return
	end
	local overflow = player_inv:add_item("main", stack:take_item(1))
	stack:add_item(overflow)
	kiln_inv:set_stack(retlist, 1, stack)
end

--punch to return
local function on_punch(pos, node, puncher, pointed_thing)
	if not puncher
	then
		return true
	end
	local meta = minetest.get_meta(pos)
	local inv = meta:get_inventory()
	local wholestack = puncher:get_player_control().aux1


	local puncherinv = puncher:get_inventory()
	if wholestack
	then
		kiln_item_return_wholestack(inv, puncherinv)
	else
		kiln_item_return_single(inv, puncherinv)
	end
	update_infotext(meta, false)
	return true
end


--add items to the kiln inventory
-----------------------------------------------------
local function start_timer_if_needed(pos, inv)
	local timer = minetest.get_node_timer(pos)
	if timer:is_started()
	then
		--already smelting
		return
	end
	local input = inv:get_stack("in", 1)
	if input:is_empty()
	then
		return
	end
	local fuel = inv:get_stack("fuel", 1)
	if fuel:is_empty()
	then
		return
	end


	--we now know that input and fuel are not empty and that the kiln
	--is not burning already, so we can start smelting
	timer:start(KILN_TIMER_LENGTH)
	return true
end

local function can_put(to_put, input_stack, output_stack)
	local smelt_result = get_smelt_result(to_put)
	if not smelt_result
	then
		return false
	end
	return (input_stack:is_empty() or
		to_put:get_name() == input_stack:get_name()) and
		(output_stack:is_empty() or
		smelt_result.item:get_name() == output_stack:get_name())
end


local function kiln_item_add(kiln_inv, itemstack, amount)
	local rest
	if itemstack:get_name() == coal
	then
		local coalstack = kiln_inv:get_stack("fuel", 1)
		rest = coalstack:add_item(itemstack:take_item(amount))
		kiln_inv:set_stack("fuel", 1, coalstack)
	else
		local to_smelt = kiln_inv:get_stack("in", 1)
		local smelted = kiln_inv:get_stack("out", 1)
		if can_put(itemstack, to_smelt, smelted)
		then
			rest = to_smelt:add_item(itemstack:take_item(amount))
			kiln_inv:set_stack("in", 1, to_smelt)
		end
	end
	itemstack:add_item(ItemStack(rest))
end

local function kiln_item_add_single(kiln_inv, itemstack)
	kiln_item_add(kiln_inv, itemstack, 1)
end
local function kiln_item_add_wholestack(kiln_inv, itemstack)
	local stack_size = itemstack:get_count()
	kiln_item_add(kiln_inv, itemstack, stack_size)
end

--add stuff to the kiln with rightclick
local function on_rightclick(pos, node, clicker, itemstack, pointed_thing)
	local meta = minetest.get_meta(pos)
	local inv = meta:get_inventory()
	local wholestack = clicker:get_player_control().aux1

	if wholestack
	then
		kiln_item_add_wholestack(inv, itemstack)
	else
		kiln_item_add_single(inv, itemstack)
	end

	local switch_to_burning = start_timer_if_needed(pos, inv)
	if switch_to_burning
	then
		local n =
		{
			name = "kilns:kiln_on",
			param1 = node.param1,
			param2 = node.param2
		}
		minetest.swap_node(pos, n)
	end
	update_infotext(meta, false)
	return itemstack
end



--initialize metadata
------------------------------------------------------------------------

local function on_construct(pos)
	local meta = minetest.get_meta(pos)

	meta:set_float("heat", 0)
	meta:set_float("progress", 0)
	meta:mark_as_private({"heat", "progress"})

	local inv = meta:get_inventory()
	inv:set_size("fuel", 1)
	inv:set_size("in", 1)
	inv:set_size("out", 1)
	update_infotext(meta, false)
end

return on_timer, on_punch, on_rightclick, on_construct
